# coding=utf8
#phon_data class to represent an individual token of child language production
#hi
#some problems:
#need split data which is multiple words into single phon_data
#need to handle case when child form is actually longer than adult

import metrics #used for edit distance functions, etc.
import random

#each is a list of generally extrasegmental symbols to be removed
#the prefix of the name of each list indicates where the symbols originated
JULIA_SYMBOLS = ["̆", "̞", "̪", "ʷ", "̥", "̩", "ʰ", "ʴ", "ˈ", "ˌ", "ː", "͡",
                 "̠", "̰", "›", u'\u0303', u'\u0323', "ʲ", "‹", "˞", "˺"] 

SONYA_SYMBOLS = ["̂", u'\u0303', '\xcc\x81', "̆", "̰", "˺", "̚", "̥", "̤",
                 "̩", "̪", "̬", "ʰ", "̹", "ʷ", "‹", "›", "͡", "ˈ", "", "ˌ"
                 "ː", "ˑ", "ˈ", "ˌ", "̩"]

AMAHL_SYMBOLS = ["˺", "ʲ", "̥", "̩", "̪", "ʰ", "ʴ", "ʷ", "‹", "›", "ʼ",
                 '\xcb\x88', u'\uf176', "ˌ", "ː", "˞", "ˠ", "ˈ", u'\u0303', "ˌ"]

CLARA_SYMBOLS = ["̆", "̪", u'\uf176', '\xcc\x81', "̥", "ʰ", "ˈ", "ˌ", "ː", "͡", "̈",
                 "̜", "̤", "*", "̰", "›", u'\u0303', "̯", "ʲ", "‹", "̻", "ˈ",
                 "ː", "̥"]

THEO_SYMBOLS = ["̆", "̪", u'\uf1b1', "ʷ", "̺", "̝", "̥", "̩", "ʰ", "̹", "ˈ", "ˌ",
                "ː", "̰", "›", "ˑ", "ˡ", u'\u0303', u'\uf1a8', "̯", "ʲ", "ʶ", "‹",
                "̻", "˺", "̥", "̩", ".", "̰", "ʲ", "ˈ", u'\u0323', "ː"]

ALL_SYMBOLS =  list(set(JULIA_SYMBOLS + SONYA_SYMBOLS + AMAHL_SYMBOLS + CLARA_SYMBOLS +
                THEO_SYMBOLS))
#u'\u0303' represents tilde, e.g. ̃a
#'\xcc\x81' represents "́ " 
#u'\u0323' represents "̣ "
#'\xcb\x88' represents "ˈ", which gives an error when encoded in UTF8
#u'\uf1b1' from THEO prints as a  (unknown symbol)
#u'\uf1a8' from THEO prints as a  (unknown symbol)
#u'\uf176' from CLARA prints as a  (unknown symbol)
#u'\uf176' from AMAHL prints as a  (unknown symbol)
#u'\uf176' from SONYA prints as a  (unknown symbol)

class phon_data():
    """
    A representation of an individual token of child language

    Args:
        None (initialized by filling in fields manually)
    Fields:
        word: the orthographical representation of the token
        child: the phonological transcription of the child's utterance
        adult: the target phonological transcription (how an adult says it)
        type: the condition under which the child spoke (imitation, etc.)
        age: the physical age of the child at time of utterance
        name: the name of the child/subject
        lang: the language of the child
        title: the source transcript of the token
        child_symbols: a list of all unique child segments over the token
        adult_symbols: a list of all unique adult segments over the token

    """
    def __init__(self):
        self.word = ""
        self.child = ""
        self.adult = ""
        self.type = ""
        self.age = ""
        self.name = ""
        self.lang = ""
        self.title = ""
        self.child_symbols = []
        self.adult_symbols = []
    def __repr__(self):
        return ("word: %s, child form: %s, adult form: %s"
                % (self.word, self.child.encode("utf8"),
                   self.adult.encode("utf8")))
    def trim(self):
        """
        Removes all symbols that are in ALL_SYMBOLS

        """
        for symbol in ALL_SYMBOLS:
            if isinstance(symbol, str):
                self.child = self.child.replace(symbol.decode("utf8"), "")
                self.adult = self.adult.replace(symbol.decode("utf8"), "")
            elif isinstance(symbol, unicode):
                self.child = self.child.replace(symbol, "")
                self.adult = self.adult.replace(symbol, "")

    def genesee_clean(self):
        self.child = self.child.replace("amp;", "")
    
    def get_symbols(self):
        """Returns a 2-tuple (x,y)
        where x is a list of IPA symbols of the child form
        and y is a list of IPA symbols of the adult form"""
        self.child_symbols = [x for x in self.child if x != " "]
        self.adult_symbols = [x for x in self.adult if x != " "]
        return[self.child_symbols, self.adult_symbols]

    def split(self):
        """Turns a multiword token each
        into many single world tokens"""
        child_tokens = self.child.split(" ")
        adult_tokens = self.adult.split(" ")
        word_tokens = self.word.split(" ")
        new_data = []
        x = 0
        for token in child_tokens:
            p = phon_data()
            p.title = self.title
            p.age = self.age
            p.name = self.name
            p.lang = self.lang
            p.child = token
            if x < len(adult_tokens):
                p.adult = adult_tokens[x]
            if x < len(word_tokens):
                p.word = word_tokens[x]
            p.type = self.type
            p.get_symbols()
            new_data.append(p)
            x += 1
        return new_data
    
    def align(self):
        """
        Returns an optimally aligned sequence for HMM training

        Args:
            None
        Returns:
            (x,y): where x is the child form aligned, and y the adult

        """
        sequence = random.choice(metrics.edit_distance_alignment(self.child, self.adult))
        form = []
        for symbol in range(0, len(sequence[0])):
            form.append((sequence[0][symbol], sequence[1][symbol]))
        return form
    
    def hmm_form(self):
        """Returns a list of 2-tuples (x,y)
        where x is the adult IPA value, and y is the child
        IPA value"""
        form = []
        if len(self.child_symbols) == len(self.adult_symbols):
            for x in range(0, len(self.child_symbols)):
                form.append((self.child_symbols[x], self.adult_symbols[x]))
        elif len(self.child_symbols) < len(self.adult_symbols):
            for x in range(0, len(self.child_symbols)):
                form.append((self.child_symbols[x], self.adult_symbols[x]))
        else:
            for x in range(0, len(self.adult_symbols)):
                form.append((self.child_symbols[x], self.adult_symbols[x]))
        return form

    def crfsuite_form(self, reverse = False):
        """Converts the phon_data into a format that
        CRFsuite can recognize for training or testing"""
        CHILD = 0
        ADULT = 1
        if reverse:
            CHILD = 1
            ADULT = 0
        form = self.align() #get the HMM form
        crfString = ""
        x = 0
        for pair in form:
            if x == 0 and len(form) > 1:
                crfString += pair[CHILD] + "\t" + "Adult=" + pair[ADULT] + "\t" + "__BOS__" + "\n" #+ "\t" + "NextAdult=" + form[x+1][ADULT] + "\n"
            elif x == 0 and len(form) == 1:
                crfString += pair[CHILD] + "\t" + "Adult=" + pair[ADULT] + "\t" + "__BOS__" + "\n"
            elif x < len(form) - 1:
                crfString += pair[CHILD] + "\t" + "Adult=" + pair[ADULT] + "\n" #+ "\t" + "NextAdult=" + form[x+1][ADULT] + "\n"
            else:
                crfString += pair[CHILD] + "\t" + "Adult=" + pair[ADULT] + "\t" + "__EOS__" + "\n"
            x += 1
        return crfString

    def maxent_form(self):
        """Converts the phon_data to a format
        that can be used by Hayes and Wislon (2008) maxent"""
        maxentString = ""
        for x in range(0, len(self.child)):
            maxentString += self.child[x] + " "
        maxentString += "10" + "\n"
        return maxentString
    
        
